package GRBL;

use Device::SerialPort;
use Time::HiRes qw/sleep/;
use Data::Dumper;


=pod

=head1 NAME

GRBL - Module to connect a GRBL device and send commands.

=head1 SYNOPSIS

  my $grbl = GRBL->new();
  $grbl->connect('/dev/ttyUSB0') 
   or die $grbl->error();
  my $ok = $grbl->cmd('G0 X0');
  my $ok = $grbl->load('example.nc');

=head1 DESCRIPTION

Package to manage to connect to a serial grbl device and send commands. 

=head2 B<$grbl = GRBL-E<gt>new(\%Params)>;

Create a new GRBL object.

=cut


sub new {
   my $class = shift;
   my $args = shift;
   my $self  = { };

   bless($self, $class);

   $self->{baudrate} = delete $args->{baudrate} || 9600;
   $self->{databits} = delete $args->{databits} || 8;
   $self->{parity}   = delete $args->{parity}   || 'none';
   $self->{stopbits} = delete $args->{stopbits} || 1;

   $self->{startsequence} = delete $args->{startsequence} || ['G20', 'G90'];

   $self->{debug} = delete $args->{debug} || '';

   return $self;
}


=pod

=back

=head2 B<$cluster-E<gt>connect('/dev/device', 9600)>

Connect to a grbl device. Baudrate are optional!

=cut

sub connect {
   my $obj = shift || die "No object!";
   my $dev = shift || die "No Drevice";
   my $baudrate = shift || $obj->{baudrate};

   return 1 if($obj->{PORT});

   #Open port
   $obj->{PORT} = Device::SerialPort->new($dev)
      or die "Can't open $dev: $!\n";
   
   # 19200, 81N on the USB ftdi driver
   $obj->{PORT}->baudrate( $obj->{baudrate}  );
   $obj->{PORT}->databits( $obj->{databits}  );
   $obj->{PORT}->parity(   $obj->{parity}  );
   $obj->{PORT}->stopbits( $obj->{stopbits}  );

   $obj->{PORT}->read_const_time(100); # timeouts
   $obj->{PORT}->read_char_time(100); # dto.

   $obj->{PORT}->write_settings 
      || die "no settings";

   my $erg = $obj->cmd($obj->{startsequence});

   return $erg;
}

=pod

=back

=head2 B<$cluster-E<gt>close()>

Close port from grbl device.

=cut

sub close {
   my $obj = shift || die "No object!";

   return 1 if(! $obj->{PORT});

   #close port
   undef $obj->{PORT};

   return 1;
}

=pod

=back

=head2 B<$cluster-E<gt>connected()>

Test to connected device.

=cut

sub connected {
   my $obj = shift || die "No object!";

   return 1 if($obj->{PORT});

   return 0;
}


=pod

=back

=head2 B<$cluster-E<gt>cmd(['G0 X0', 'G0 Y0'])>

Send command to grbl device.

=cut

sub cmd{
   my $obj = shift || die "No object!";
   my $cmd = shift || die "No Command";
 
   my $commands = (ref $cmd eq 'ARRAY' ? $cmd : [$cmd]);

   #Open port
   my $port = $obj->{PORT}
      or die "Device is not connected!";
 
   my $return = [];

   foreach my $command (@$commands){
      $return = $obj->get($command);
   }

   return $return;
}

=pod

=back

=head2 B<$cluster-E<gt>get()>

get output from grbl device.

=cut

sub get{
   my $obj = shift || die "No object!";
   my $cmd = shift || '';
   
   my $return = [];

   #Open port
   my $port = $obj->{PORT}
      or die "Device is not connected!";

   $port->lookclear;

   $port->write("$cmd\n") if($cmd);
   sleep 0.2;
   while(1){
      my $answer = $port->lookfor();
      $answer =~ s/\r+//g;
      $answer =~ s/\n+//g;
      push(@{$return}, $answer)
         if($answer);

      if($cmd eq '?' and $answer =~ /^\<\S+\,+/){
         return [$answer];
      }

      if($answer =~ /^(ok|start|Grbl|error)+/){
         last;
      }
   }
   
   return $return;
};


1;
